package saint.manager;


import java.util.Iterator;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.DialogInterface;
import android.location.Criteria;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;



import android.os.Bundle;
import android.provider.Contacts.Settings;

import saint.manager.GPSManagerListener;
import saint.manager.UnitConvert;


public class GpsManager {
	
	 //Location info
	 private double speed;
	 private double accuracy;
	 private double longitude;
	 private double latitude;
	 private double altitude;
	 private double distance;
	 private Location lastLocation;
	 // Signal and satellite info 
	 private int satelliteInUse;
	 private int satelliteInView;
	 private double snr;
	 // Speed unit identify
	 private int speedUnit;
	 private int distantUint;
	 // Location Provider (GPS or Network)
	 private String providers;
	 
	 //Class in use
	 private LocationManager locationManager;
	 private LocationListener locationListener;
	 private GpsStatus.Listener gpsListener;
	 private GPSManagerListener gpsManagerListener;
	 private Context appContext;
	 
	 // Static Member
	 private static final int gpsMinTime = 500;
	 private static final int gpsMinDistance = 0;
	 
	 
	 public static final int GPS_STATUS_UPDATE = 0;
	 public static final int GPS_LOCATION_UPDATE = 1;
	 	 
	 
	 //============ Public Method =============================================
	 public GpsManager(final Context context) {
		 this.appContext = context;
		 locationManager = (LocationManager) appContext.getSystemService(Context.LOCATION_SERVICE);
		 locationListener = null;
		 gpsListener = null;
		 gpsManagerListener = null;
		 lastLocation = null;
		 
		 speed = 0;
		 accuracy = 0;
		 longitude = 0;
		 latitude = 0;
		 altitude = 0;
		 distance = 0;
		 
		 snr = 0;
		 satelliteInUse = 0;
		 satelliteInView = 0;
		 
		 speedUnit = UnitConvert.KM_PER_HOUR;
		 distantUint = UnitConvert.KILOMETER;
	 }
	 
	 public void startGPS() {
		 final Criteria criteria = new Criteria();
		 
		 if(locationManager == null) {
			 locationManager = (LocationManager) appContext.getSystemService(Context.LOCATION_SERVICE);
		 }
		 
		 criteria.setAccuracy(Criteria.ACCURACY_FINE);
		 criteria.setSpeedRequired(true);
		 criteria.setAltitudeRequired(true);
		 criteria.setBearingRequired(false);
		 criteria.setCostAllowed(true);
		 criteria.setPowerRequirement(Criteria.POWER_LOW);
		 
		 // Get best Provider
		 providers = locationManager.getBestProvider(criteria, true);
		 
		 // Get GPS status update
		 //gpsListener = new GpsListenner();
		 
		 //locationManager.addGpsStatusListener(gpsListener);
		 
		 // Request location Update 
		 locationListener = new LocationCallback();
		 if(providers != null && providers.length() > 0) {
			locationManager.requestLocationUpdates(providers, gpsMinTime, gpsMinDistance, locationListener);
		 }
		 else {
			 final List<String> proviers = locationManager.getProviders(true);
			 for(final String provider : proviers) {
				 locationManager.requestLocationUpdates(provider, gpsMinTime, gpsMinDistance, locationListener);
			 }
		 }
		 
	 }
	 
	 public void stopGPS() {
		 try {
			 if(locationManager != null && locationListener != null) {
				 locationManager.removeUpdates(locationListener);
			 }
			 if(gpsListener != null) {
				 locationManager.removeGpsStatusListener(gpsListener);
			 }
		 }
		 catch (final Exception ex) {
		 }
	 }
	 
	 public GpsInfo isHasGpsInfo() {
		Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		getGpsLocationInfo(location);
		if((accuracy != 0) || (latitude != 0) || (longitude != 0) || (altitude != 0)) {
			return new GpsInfo(0, accuracy, longitude, latitude, altitude, 0);
		}
		else { return null; }
		
	}
	 
	 public void checkGPSEnable() {
		 boolean isGPSEnable;
		 isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
		 
		 if(!isGPSEnable) {
			 AlertDialog.Builder alertBuilder = new AlertDialog.Builder(appContext)
			 .setMessage("GPS is not enable")
			 .setMessage("GPS is not enable. Do you want to go settting?")
			 .setPositiveButton("OK", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					Intent settingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
					appContext.startActivity(settingIntent);
					
				}
			})
			.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dialog.cancel();
					
				}
			});
			 
			 alertBuilder.show(); 
		 }
	}
	 
	 //========================================================================
	 
	 //============ Private Method ============================================
	 private void getGpsStatusInfo() {
		 boolean isGps = providers.equals(LocationManager.GPS_PROVIDER);
		 if(isGps) {

			 GpsStatus gpsStatus = locationManager.getGpsStatus(null);
			 GpsSatellite gpsSat;
			 float snrRatio = 0;
			 int satNumber = 0;
			 int satUse = 0;
			 if(gpsStatus != null) {
				 Iterable<GpsSatellite>satellites = gpsStatus.getSatellites();
				 Iterator<GpsSatellite>sat = satellites.iterator();
				 
				 while(sat.hasNext()) {
						gpsSat = sat.next();
						if(gpsSat.getSnr() != 0) {
							snrRatio += gpsSat.getSnr();
							satUse++;
						}
						satNumber++;
					}
				 
				 satNumber--;
				 satelliteInView = satNumber;
				// Mean of SNR
				 snrRatio = snrRatio/satUse;
				 
				 snr = snrRatio;
				 
				 satelliteInUse = satUse;
			 }
		 }
	 }
	 
	 private void getGpsLocationInfo(Location location) {
		float _speed = 0;
		
		// Get Speed
		_speed = location.getSpeed(); 
		speed = UnitConvert.ConvertSpeed(_speed, speedUnit);
		
		// Get Accuracy
		accuracy = location.getAccuracy(); 
		
		// Get Altitude
		altitude = location.getAltitude(); 
		
		// First location detect
		if(lastLocation == null) {
			lastLocation = location;
			distance = 0;
			speed = 0;
		}
		else {
			//Check last location has info
			if(_speed != 0) {
				double dis = 0;
					dis = lastLocation.distanceTo(location);
				if(dis > 1) {
					// Convert distance unit
					distance += UnitConvert.ConvertDistance(dis, distantUint);
					// Get last location
					lastLocation = location;				
				}		
			}
		}
		// Get longitude
		longitude = location.getLongitude();
		// Get latitude
		latitude = location.getLatitude();
		
	}
	 //========================================================================
	 
	 //=========== Get and Set attribute ======================================
	 public double getSpeed() {
		 return speed;
	 }
	 
	 public double getAccuracy() {
		return accuracy;
	}
	 
	 public double getLongitude() {
		 return longitude;
	 }
	
	 public double getLatitude() {
		 return latitude;
	 }
	
	 public double getAltitude() {
		 return altitude;
	 }
	 
	 public double getDistance() {
		 return distance;
	 }
	 
	 public double getSNR() {
		 return snr;
	 }
	 
	 public int getSatelliteInUse() {
		 return satelliteInUse;
	 }
	 
	 public int getSatelliteInView() {
		 return satelliteInView;
	 }
	
	 public void setGPSManagerListener(GPSManagerListener listener) {
	 	 this.gpsManagerListener = listener;
	 }
	 
	 public int getSpeedUnit() {
	    return speedUnit;
	 }
	 
	 public void setSpeedUnit(int value) {
		speedUnit = value;
	 }
	 
	 public int getDistantUnit() {
		 return distantUint;
	 }
	 
	 public void setDistanceUnit(int value) {
		distantUint = value;
		distance = UnitConvert.ConvertDistance(distance, distantUint);
	}
	 
	 //=========================================================================
	
	 //========== Private class implement =====================================
	 private class LocationCallback implements LocationListener {
		 public void onLocationChanged(Location location) {
			 getGpsLocationInfo(location);
			 getGpsStatusInfo();
			// Callback to activity
			if(gpsManagerListener != null) {
				gpsManagerListener.onGPSUpdate(GPS_LOCATION_UPDATE , new GpsInfo(speed, accuracy, longitude, latitude, altitude, distance));
			}
		 }

		 public void onProviderDisabled(String provider) {
		
		 }

		 public void onProviderEnabled(String provider) {
			
		 }

		 public void onStatusChanged(String provider, int status, Bundle extras) {
			
		 }
			
	 }

	 private class GpsListenner implements GpsStatus.Listener {
		 public void onGpsStatusChanged(int event) {
			 if(event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
				 getGpsStatusInfo();
				 if(gpsManagerListener != null) {
					// Callback to activity
					 gpsManagerListener.onGPSUpdate(GPS_STATUS_UPDATE , null);
				 }
			}
			
		}		 
	 }
	 //========================================================================
}




